﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using CsvHelper;
using CsvHelper.Configuration.Attributes;
using DevExpress.Xpf.Grid;
using OfficeOpenXml;
using static System.Math;

namespace CableForceCalc
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //
            GridControlNodeData.ItemsSource = NodeData;
            GridControlElemData.ItemsSource = ElemData;
        }

        public ObservableCollection<Node> NodeData { get; } = new ObservableCollection<Node>();
        public ObservableCollection<Elem> ElemData { get; } = new ObservableCollection<Elem>();

        private void GridControlNodeData_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            NodeData.Clear();
        }

        private void GridControlElemData_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            ElemData.Clear();
        }

        /*
        private void Button_Click(object sender, RoutedEventArgs arg)
        {
            var folder = @"F:\坚果云\大桥院\马鞍山大桥\建模\20191202\斜拉索初索力";
            var nodeFileName = "CableNode.txt";
            var elemFileName = "CableElem.txt";
            //var forceFileName = "CableForce.txt";
            var forceExcelFileName = "斜拉索初拉力.xlsx";

            var beamWeight = double.Parse(TextBoxBeamWeight.Text);
            var f1 = double.Parse(TextBoxMajorFactor1.Text);
            var f2 = double.Parse(TextBoxMajorFactor2.Text);
            var f3 = double.Parse(TextBoxMajorFactor3.Text);
            var majorFactors = new[] {f1, f2, f3, f3, f2, f1};

            f1 = double.Parse(TextBoxMinorFactor1.Text);
            f2 = double.Parse(TextBoxMinorFactor2.Text);
            f3 = double.Parse(TextBoxMinorFactor3.Text);
            var minorFactors = new[] {f1, f2, f3, f3, f2, f1};

            Dictionary<int, Node> nodeDict;
            using (var reader = new StreamReader(Path.Combine(folder, nodeFileName)))
            using (var csv = new CsvReader(reader))
            {
                csv.Configuration.HasHeaderRecord = false;
                csv.Configuration.Delimiter = "\t";
                nodeDict = csv.GetRecords<Node>()
                    .ToDictionary(n => n.Id, n => n);
            }

            List<Elem> elemList;
            using (var reader = new StreamReader(Path.Combine(folder, elemFileName)))
            using (var csv = new CsvReader(reader))
            {
                csv.Configuration.HasHeaderRecord = false;
                csv.Configuration.Delimiter = "\t";
                elemList = csv.GetRecords<Elem>()
                    .OrderBy(el => GetUpperLowerNode(el, nodeDict).LowerNode.X).ToList();
            }

            var elemSections = new List<List<Elem>>();
            List<Elem> curElemSection = null;
            var prevSign = -1;
            foreach (var el in elemList)
            {
                var (upperNode, lowerNode) = GetUpperLowerNode(el, nodeDict);
                var sign = upperNode.X > lowerNode.X ? 1 : -1;
                if (sign == prevSign)
                {
                    Debug.Assert(curElemSection != null);
                    curElemSection.Add(el);
                }
                else
                {
                    curElemSection = new List<Elem> {el};
                    elemSections.Add(curElemSection);
                }

                prevSign = sign;
            }

            var initCableForces = elemList.Select(el =>
            {
                var n1 = nodeDict[el.N1];
                var n2 = nodeDict[el.N2];
                var dx = Math.Abs(n1.X - n2.X);
                var dy = Math.Abs(n1.Y - n2.Y);
                var dz = Math.Abs(n1.Z - n2.Z);
                var force = beamWeight * Math.Sqrt(dx * dx + dy * dy + dz * dz) / dz;
                return new ElemForce {ElemId = el.Id, Force = force};
            }).ToDictionary(ef => ef.ElemId, ef => ef);


            Debug.Assert(minorFactors.Length == elemSections.Count);

            double Pulse(double x) => x < 0.5 ? x * 2D : 2D - x * 2D;

            var finalCableForces = new List<ElemForce>();
            for (var i = 0; i < elemSections.Count; i++)
            {
                var elemSection = elemSections[i];
                var majorFactor = majorFactors[i];
                var minorFactor = minorFactors[i];
                var elemCount = elemSection.Count;
                for (var j = 0; j < elemCount; j++)
                {
                    var elemId = elemSection[j].Id;
                    var initForce = initCableForces[elemId].Force;
                    var finalForce = initForce * (majorFactor + minorFactor * Pulse((double) j / elemCount));
                    finalCableForces.Add(new ElemForce {ElemId = elemId, Force = finalForce});
                }
            }

            finalCableForces = finalCableForces.OrderBy(ef => ef.ElemId).ToList();

            //
            //using (var writer = new StreamWriter(Path.Combine(folder, forceFileName), false))
            //using (var csv = new CsvWriter(writer))
            //{
            //    csv.Configuration.HasHeaderRecord = false;
            //    csv.Configuration.Delimiter = "\t";
            //    csv.WriteRecords(finalCableForces.OrderBy(ef => ef.ElemId));
            //}

            using (var excel = new ExcelPackage(new FileInfo(Path.Combine(folder, forceExcelFileName))))
            {
                var cells = excel.Workbook.Worksheets[1].Cells;
                for (var i = 0; i < finalCableForces.Count; i++)
                {
                    var ef = finalCableForces[i];
                    cells[i + 1, 6].Value = ef.ElemId;
                    cells[i + 1, 7].Value = ef.Force;
                }

                excel.Save();
            }

            //
            MessageBox.Show("Done!");
        }
        */

        private static (Node UpperNode, Node LowerNode) GetUpperLowerNode(Elem el,
            IReadOnlyDictionary<int, Node> nodeDict)
        {
            var n1 = nodeDict[el.N1];
            var n2 = nodeDict[el.N2];
            return n1.Z > n2.Z ? (n1, n2) : (n2, n1);
        }

        private void ButtonGenerateMct_OnClick(object sender, RoutedEventArgs e)
        {
            var beamWeight = (double) (SpinEditTotalWeight.Value / SpinEditTotalBeamCount.Value);
            var deadLoad2 = (double) (SpinEditDeadLoad2.Value);

            var f1 = double.Parse(SpinEditMajorFactor1.Text);
            var f2 = double.Parse(SpinEditMajorFactor2.Text);
            var f3 = double.Parse(SpinEditMajorFactor3.Text);
            var majorFactors = new[] {f1, f2, f3, f3, f2, f1};

            //var minorFactors = new[] { f1, f2, f3, f3, f2, f1 };

            var nodeDict = NodeData.ToDictionary(n => n.Id, n => n);
            var elemList = ElemData.OrderBy(el => GetUpperLowerNode(el, nodeDict).LowerNode.X).ToList();

            var elemSections = new List<List<Elem>>();
            List<Elem> curElemSection = null;
            var prevSign = -1;
            foreach (var el in elemList)
            {
                var (upperNode, lowerNode) = GetUpperLowerNode(el, nodeDict);
                var sign = upperNode.X > lowerNode.X ? 1 : -1;
                if (sign == prevSign)
                {
                    Debug.Assert(curElemSection != null);
                    curElemSection.Add(el);
                }
                else
                {
                    curElemSection = new List<Elem> {el};
                    elemSections.Add(curElemSection);
                }

                prevSign = sign;
            }

            var initCableForces = elemList.Select(el =>
            {
                var n1 = nodeDict[el.N1];
                var n2 = nodeDict[el.N2];
                var dz = Abs(n1.Z - n2.Z);
                var force = ((beamWeight + deadLoad2) / 2) * n1.DistTo(n2) / dz;
                return new ElemForce {ElemId = el.Id, Force = force};
            }).ToDictionary(ef => ef.ElemId, ef => ef);

            Debug.Assert(majorFactors.Length == elemSections.Count);

            double Pulse(double x) => x < 0.5 ? x * 2D : 2D - x * 2D;

            //
            var finalCableForces = new List<ElemForce>();
            for (var i = 0; i < elemSections.Count; i++)
            {
                var elemSection = elemSections[i];
                var majorFactor = majorFactors[i];
                //var minorFactor = minorFactors[i];
                var elemCount = elemSection.Count;
                for (var j = 0; j < elemCount; j++)
                {
                    var elemId = elemSection[j].Id;
                    var initForce = initCableForces[elemId].Force;
                    //var finalForce = initForce * (majorFactor + minorFactor * Pulse((double) j / elemCount));
                    var finalForce = initForce * majorFactor;
                    finalCableForces.Add(new ElemForce {ElemId = elemId, Force = finalForce});
                }
            }

            finalCableForces = finalCableForces.OrderBy(ef => ef.ElemId).ToList();

            //
            TextBoxCableForce.Text = string.Join(Environment.NewLine, finalCableForces.Select(f => $"{f.Force:F0}"));
        }

        private void ButtonCopy_OnClick(object sender, RoutedEventArgs e)
        {
            var text = TextBoxCableForce.Text;
            if (string.IsNullOrWhiteSpace(text))
                return;

            Clipboard.SetText(text);
        }
    }

    /*
public class Node
{
    [Index(0)] public int Id { get; set; }

    [Index(1)] public double X { get; set; }
    [Index(2)] public double Y { get; set; }
    [Index(3)] public double Z { get; set; }

    public double DistTo(Node other) 
        => Sqrt(Pow(X - other.X, 2) + Pow(Y - other.Y, 2) + Pow(Z - other.Z, 2));
}

public class Elem
{
    [Index(0)] public int Id { get; set; }
    [Index(6)] public int N1 { get; set; }
    [Index(7)] public int N2 { get; set; }
}
*/

    public class Node
    {
        public int Id { get; set; }
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public double DistTo(Node other)
            => Sqrt(Pow(X - other.X, 2) + Pow(Y - other.Y, 2) + Pow(Z - other.Z, 2));
    }

    public class Elem
    {
        public int Id { get; set; }
        public int N1 { get; set; }
        public int N2 { get; set; }

        public string Field2 { get; set; }
        public string Field3 { get; set; }
        public string Field4 { get; set; }
        public string Field5 { get; set; }
        public string Field6 { get; set; }
    }

    public class ElemForce
    {
        public int ElemId { get; set; }
        public double Force { get; set; }
    }
}